/*
	project01.c
	Author: Pedro J. Toro
	E-Mail: ptoro1@worcester.edu
	Program: Project01
	CSC 8009 Network Security
	03/05/2008

	This program uses the simplified DES algorithm for the encryption and decryption of input
	files in conjunction with a 10-bit key.
	
*/

#include "project01.h"
#include <stdio.h>				/* standard header file */
#include <string.h>				/* string manipulation */
#include <stdlib.h>				/* for exit() */
#include <ctype.h>				/* for strlwr() */


int main(int argc, char **argv) {
	FILE* infile;
	FILE* outfile;
	unsigned char k1[BLOCKSIZE];
	unsigned char k2[BLOCKSIZE];
	unsigned char c, enc, dec;

	if (argc == 5) {
		if (!(validKeyFormat(argv[2]))) {
			printf("\n");
			printf("Not a valid 10 bit key.\n");
			usageSummary();
			exit(1);
		}

		genKeys(argv[2], k1, k2);

		/* open and try to read input file */
		infile = fopen(argv[3], "r");

		if (infile == 0) {
			printf("\n");
			printf("Could not open \"%s\"\n", argv[3]);
			usageSummary();
			exit(1);
		}

		/* open and try to write to output file */
		outfile = fopen(argv[4], "w");	
		if (outfile == NULL) {
			printf("\n");
			printf ("Cannot write to \"%s\"\n", argv[4]);
			usageSummary();
			exit(1);
		}

		if (strcmp(strlwr(argv[1]), ENCRYPT) == 0) {
			c = fgetc(infile);

			while ((char)c != -1) {
				enc = encrypt(c, k1, k2);
				printf("%c", enc);
				fprintf(outfile, "%c", enc);

				c = fgetc(infile);
			}
		} else if (strcmp(strlwr(argv[1]), DECRYPT) == 0) {
			c = fgetc(infile);

			while ((char)c != -1) {
				dec = encrypt(c, k2, k1);
				printf("%c", dec);
				fprintf(outfile, "%c", dec);

				c = fgetc(infile);
			}
		} else {
			/* not proper usage */
			usageSummary();
			exit(1);
		}	

		/* close file */
		fclose(infile);
	} else {
		/* not proper usage */
		usageSummary();
		exit(1);
	}

	return 0;
}

/* Displays the proper syntax for using the program.
*/
void usageSummary() {
	printf("\n");
	printf("To encrypt a message:\n");
	printf("usage: <program name> -e <10-bit key> <input file to encrypt> <output encrypted file>\n");
	printf("\n");
	printf("To decrypt a message:\n");
	printf("usage: <program name> -d <10-bit key> <input file to decrypt> <output decrypted file>\n");
	printf("\n");
}

/* Checks if the key is a valid format.
*/
int validKeyFormat(unsigned char key[]) {
	int validKey = FALSE;
	int x;
	int size = strlen(key);

	if (strlen(key) == KEYSIZE) {
		for (x=0; x<size; x++) {
			if (isdigit(key[x])) {
				key[x] = key[x] - 48;
			} else {
				return validKey;
			}
		}

		validKey = TRUE;
	}

	return validKey;
}

/* Converts a character into an 8 "bit" representation using an array of characters.
*/
void convertTo8Bits(unsigned char letter, unsigned char _8bits[]){
	_8bits[0] = (letter >> 7) & 00000001;
	_8bits[1] = (letter >> 6) & 00000001;
	_8bits[2] = (letter >> 5) & 00000001;
	_8bits[3] = (letter >> 4) & 00000001;
	_8bits[4] = (letter >> 3) & 00000001;
	_8bits[5] = (letter >> 2) & 00000001;
	_8bits[6] = (letter >> 1) & 00000001;
	_8bits[7] = letter & 00000001;
}

/* Converts an array of 8 characters into a character.
*/
unsigned char convertToChar(unsigned char _8bits[]){
	unsigned char letter;

	letter = _8bits[0] << 7;
	letter |= _8bits[1] << 6;
	letter |= _8bits[2] << 5;
	letter |= _8bits[3] << 4;
	letter |= _8bits[4] << 3;
	letter |= _8bits[5] << 2;
	letter |= _8bits[6] << 1;
	letter |= _8bits[7];

	return letter;
}

/*********** S-DES Key Generation ***********/
/* Generates the two keys used for encryption/decryption.
*/
void genKeys(unsigned char key[], unsigned char *k1, unsigned char *k2) {
	unsigned char p10[KEYSIZE];

	// copy key to p10
	p10[0] = key[0];
	p10[1] = key[1];
	p10[2] = key[2];
	p10[3] = key[3];
	p10[4] = key[4];
	p10[5] = key[5];
	p10[6] = key[6];
	p10[7] = key[7];
	p10[8] = key[8];
	p10[9] = key[9];

	// P10 permutation
	P10(p10);

	// Left Shift 1
	LS(p10);
	
	// P8 permutation to k1
	P8(p10, k1);

	// Left Shift 2
	LS(p10);
	LS(p10);
	
	// P8 permutation to k2
	P8(p10, k2);
}

/* P10 permutaion
*/
void P10(unsigned char _10bits[]) {
	unsigned char temp[KEYSIZE];

	temp[0] = _10bits[0];
	temp[1] = _10bits[1];
	temp[2] = _10bits[2];
	temp[3] = _10bits[3];
	temp[4] = _10bits[4];
	temp[5] = _10bits[5];
	temp[6] = _10bits[6];
	temp[7] = _10bits[7];
	temp[8] = _10bits[8];
	temp[9] = _10bits[9];

	_10bits[0] = temp[2];
	_10bits[1] = temp[4];
	_10bits[2] = temp[1];
	_10bits[3] = temp[6];
	_10bits[4] = temp[3];
	_10bits[5] = temp[9];
	_10bits[6] = temp[0];
	_10bits[7] = temp[8];
	_10bits[8] = temp[7];
	_10bits[9] = temp[5];
}

/* Circular left shift
*/
void LS(unsigned char _10bits[]) {
	unsigned char temp;

	// store leftmost "bit" value of the first 5 "bits" for rotaion
	temp = _10bits[0];

	// rotate left 1 position
	_10bits[0] = _10bits[1];
	_10bits[1] = _10bits[2];
	_10bits[2] = _10bits[3];
	_10bits[3] = _10bits[4];
	_10bits[4] = temp;

	// store leftmost "bit" value of the second 5 "bits" for rotaion
	temp = _10bits[5];

	// rotate left 1 position
	_10bits[5] = _10bits[6];
	_10bits[6] = _10bits[7];
	_10bits[7] = _10bits[8];
	_10bits[8] = _10bits[9];
	_10bits[9] = temp;
}

/* P8 permutation
*/
void P8(unsigned char p10[], unsigned char p8[]) {
	p8[0] = p10[5];
	p8[1] = p10[2];
	p8[2] = p10[6];
	p8[3] = p10[3];
	p8[4] = p10[7];
	p8[5] = p10[4];
	p8[6] = p10[9];
	p8[7] = p10[8];
}
/*********** S-DES Key Generation ***********/

/*********** S-DES Encryption/Decryption ***********/
/* Encrypt the given plaintext and return a cyphertext.
*/
unsigned char encrypt(unsigned char plaintext, unsigned char *k1, unsigned char *k2) {
	unsigned char ip[BLOCKSIZE];

	convertTo8Bits(plaintext, ip);
	IP(ip);

	fK(ip, k1);

	SW(ip);

	fK(ip, k2);

	IP_inv(ip);

	return convertToChar(ip);
}

/* IP permutation
*/
void IP(unsigned char _8bits[]) {
	unsigned char temp[BLOCKSIZE];

	temp[0] = _8bits[0];
	temp[1] = _8bits[1];
	temp[2] = _8bits[2];
	temp[3] = _8bits[3];
	temp[4] = _8bits[4];
	temp[5] = _8bits[5];
	temp[6] = _8bits[6];
	temp[7] = _8bits[7];

	_8bits[0] = temp[1];
	_8bits[1] = temp[5];
	_8bits[2] = temp[2];
	_8bits[3] = temp[0];
	_8bits[4] = temp[3];
	_8bits[5] = temp[7];
	_8bits[6] = temp[4];
	_8bits[7] = temp[6];
}

/* fK function
*/
void fK(unsigned char ip[], unsigned char k[]) {
	unsigned char ep[BLOCKSIZE];
	unsigned char fk[BLOCKSIZE/2];
	unsigned char temp[BLOCKSIZE/2];

	unsigned char p4[BLOCKSIZE/2];
	unsigned char s0[BLOCKSIZE/4];
	unsigned char s1[BLOCKSIZE/4];

	// store 4 leftmost "bits" to fk
	fk[0] = ip[0];
	fk[1] = ip[1];
	fk[2] = ip[2];
	fk[3] = ip[3];

	// store 4 rightmost "bits" to temp
	temp[0] = ip[4];
	temp[1] = ip[5];
	temp[2] = ip[6];
	temp[3] = ip[7];

	// E/P permutation
	EP(temp, ep);

	ep[0] = ep[0] ^ k[0];
	ep[1] = ep[1] ^ k[1];
	ep[2] = ep[2] ^ k[2];
	ep[3] = ep[3] ^ k[3];
	ep[4] = ep[4] ^ k[4];
	ep[5] = ep[5] ^ k[5];
	ep[6] = ep[6] ^ k[6];
	ep[7] = ep[7] ^ k[7];

	S0(((ep[0] << 1) + ep[3]), ((ep[1] << 1) + ep[2]), s0);
	S1(((ep[4] << 1) + ep[7]), ((ep[5] << 1) + ep[6]), s1);

	p4[0] = s0[0];
	p4[1] = s0[1];
	p4[2] = s1[0];
	p4[3] = s1[1];

	P4(p4);

	ip[0] = fk[0] ^ p4[0];
	ip[1] = fk[1] ^ p4[1];
	ip[2] = fk[2] ^ p4[2];
	ip[3] = fk[3] ^ p4[3];
}

/* E/P permutation
*/
void EP(unsigned char _4bits[], unsigned char ep[]) {
	ep[0] = _4bits[3];
	ep[1] = _4bits[0];
	ep[2] = _4bits[1];
	ep[3] = _4bits[2];
	ep[4] = _4bits[1];
	ep[5] = _4bits[2];
	ep[6] = _4bits[3];
	ep[7] = _4bits[0];
}

/* S0 S-Box
*/
void S0(int x, int y, unsigned char _2bits[]) {
	unsigned char s0[4][4] = {{1, 0, 3, 2},
							 {3, 2, 1, 0},
							 {0, 2, 1, 3},
							 {3, 1, 3, 2}};
	
	unsigned char temp[BLOCKSIZE] = {0};

	convertTo8Bits(s0[x][y], temp);

	_2bits[0] = temp[6];
	_2bits[1] = temp[7];
}

/* S1 S-Box
*/
void S1(int x, int y, unsigned char _2bits[]) {
	unsigned char s1[4][4] = {{0, 1, 2, 3},
							 {2, 0, 1, 3},
							 {3, 0, 1, 0},
							 {2, 1, 0, 3}};
		
	unsigned char temp[BLOCKSIZE] = {0};

	convertTo8Bits(s1[x][y], temp);

	_2bits[0] = temp[6];
	_2bits[1] = temp[7];
}

/* P4 permutation
*/
void P4(unsigned char _4bits[]) {
	unsigned char temp[BLOCKSIZE/2];
	
	temp[0] = _4bits[0];
	temp[1] = _4bits[1];
	temp[2] = _4bits[2];
	temp[3] = _4bits[3];

	_4bits[0] = temp[1];
	_4bits[1] = temp[3];
	_4bits[2] = temp[2];
	_4bits[3] = temp[0];
}

/* Interchanges the leftmost 4 "bits" with the rightmost 4 "bits".
*/
void SW(unsigned char _8bits[]) {
	unsigned char temp[BLOCKSIZE/2];

	// store 4 leftmost "bits"
	temp[0] = _8bits[0];
	temp[1] = _8bits[1];
	temp[2] = _8bits[2];
	temp[3] = _8bits[3];

	// move 4 rightmost "bits" to the left
	_8bits[0] = _8bits[4];
	_8bits[1] = _8bits[5];
	_8bits[2] = _8bits[6];
	_8bits[3] = _8bits[7];

	// move stored leftmost "bits" to the right
	_8bits[4] = temp[0];
	_8bits[5] = temp[1];
	_8bits[6] = temp[2];
	_8bits[7] = temp[3];
}

/* inverse IP permutation
*/
void IP_inv(unsigned char _8bits[]) {
	unsigned char temp[BLOCKSIZE];

	temp[0] = _8bits[0];
	temp[1] = _8bits[1];
	temp[2] = _8bits[2];
	temp[3] = _8bits[3];
	temp[4] = _8bits[4];
	temp[5] = _8bits[5];
	temp[6] = _8bits[6];
	temp[7] = _8bits[7];

	_8bits[0] = temp[3];
	_8bits[1] = temp[0];
	_8bits[2] = temp[2];
	_8bits[3] = temp[4];
	_8bits[4] = temp[6];
	_8bits[5] = temp[1];
	_8bits[6] = temp[7];
	_8bits[7] = temp[5];
}
/*********** S-DES Encryption/Decryption ***********/